package com.spmsys.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.spmsys.entity.sys.Users;
import com.spmsys.util.*;
import org.apache.commons.dbutils.QueryRunner;
import org.everit.json.schema.loader.SchemaLoader;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;


/**
 * 用户鉴权类
 */
public class Auth {
    private final static String token_secret = "KSN4WBfuuiLyVsFRYTIvOxW8dwBE3e1A";
    private final Database database;
    private Users user = null;
    private JSONObject userPackage = null;

    public Auth() {
        this.database = new Database(DBCPDataSource.getDataSource("sys"), DBCPDataSource.getDataSource("sys"));
    }

    public Auth(String owner, String createBy, String updateBy) {
        this.database = new Database(DBCPDataSource.getDataSource("sys"), DBCPDataSource.getDataSource("sys"), owner, createBy, updateBy);
    }

    public Users getUser() {
        return user;
    }

    public JSONObject getUserPackage() {
        return userPackage;
    }

    /**
     * 获取token方法
     *
     * @param expiration 过期时间
     * @return token
     */
    public String getToken(Long expiration) {
        if (user == null) {
            return null;
        }
        //返回数据
        return Token.token(user.getId(), user.getPassword(), token_secret, expiration);
    }

    /**
     * 校验token方法
     *
     * @param token token
     * @return 校验结果
     */
    public static @NotNull Boolean verify(String token) {
        //返回数据
        return Token.verify(token, token_secret);
    }

    /**
     * 通过token获取用户id方法
     *
     * @param token token
     * @return 用户id
     */
    public static String getUserIdByToken(String token) {
        //返回数据
        return Token.getUserIdByToken(token, token_secret);
    }

    /**
     * 通过token获取用户密码方法
     *
     * @param token token
     * @return 用户密码
     */
    public static String getPasswordByToken(String token) {
        //返回数据
        return Token.getPasswordByToken(token, token_secret);
    }

    /**
     * 获取用户信息方法
     *
     * @param sql sql语句
     * @return 用户信息
     */
    private Users getUserInfo(String sql) {
        //返回数据
        return (Users) this.database.runTransaction((connection) -> {
            try {
                JSONObject result = new QueryRunner().query(connection, sql, ResultSetTurnJSON::resultSetToJSONObject);
                return result.size() > 0 ? result.toJavaObject(Users.class) : null;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
    }

    /**
     * 通过id获取用户信息方法
     *
     * @param id 用户id
     * @return 用户信息
     */
    public Users getUserInfoById(String id) {
        //返回数据
        return getUserInfo("select * from users where id = '" + id + "';");
    }

    /**
     * 登录方法
     *
     * @param email    邮箱（非必填，email和phone必填其一）
     * @param phone    手机（非必填，email和phone必填其一）
     * @param password 密码
     * @return 登录结果
     */
    public JSONObject login(String email, String phone, String password) {
        JSONObject result = new JSONObject();
        try {
            Users user;
            JSONObject userPackage;
            //判断用户是否存在
            if ((user = getUserInfo("select * from users where " + (email != null ? "email" : "phone") + " = '" + (email != null ? email : phone) + "';")) == null) {
                throw new RuntimeException("用户" + (email != null ? email : phone) + "不存在");
            }
            //判断密码是否正确
            if (!Objects.equals(password, user.getPassword())) {
                throw new RuntimeException("密码错误");
            }
            //读取用户配置文件
            userPackage = JSONObject.parseObject(JSONFileOperate.readJson(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + user.getId() + ".json"));
            //保存信息
            this.user = user;
            this.userPackage = userPackage;
            //返回登录信息
            result.put("data", this.user.getId());
        } catch (Exception e) {
            result.put("errorCode", "LOGIN_FAILED");
            result.put("errorMessage", ExceptionStackTrace.getExceptionInformation(e, ExceptionStackTrace.length(e) - 1));
        }
        //返回数据
        return result;
    }

    /**
     * 注册方法
     *
     * @param record      记录
     * @param userPackage 用户配置
     * @return 注册结果
     */
    public JSONObject register(JSONObject record, JSONObject userPackage) {
        JSONObject result = new JSONObject();
        try {
            //判断密码是否填写
            if (record.getString("password") == null) {
                throw new RuntimeException("password为必填参数");
            }
            //判断邮箱是否被注册
            if (getUserInfo("select * from users where email = '" + record.getString("email") + "';") != null) {
                throw new RuntimeException(record.getString("email") + "邮箱已被注册");
            }
            //判断手机号是否被注册
            if (getUserInfo("select * from users where phone = '" + record.getString("phone") + "';") != null) {
                throw new RuntimeException(record.getString("phone") + "手机号已被注册");
            }
            //检查用户配置
            SchemaLoader.load(new org.json.JSONObject(JSONFileOperate.readJson(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + "usersPackage.json"))).validate(new org.json.JSONObject(JSONObject.toJSONString(userPackage, SerializerFeature.WriteMapNullValue)));
            //注册用户
            userPackage.put("id", database.dbCreate("users", JSONObject.parseObject(JSONObject.toJSONString(record.toJavaObject(Users.class), SerializerFeature.WriteMapNullValue))));
            JSONFileOperate.writeJson(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + userPackage.get("id") + ".json", JSONObject.toJSONString(userPackage, SerializerFeature.WriteMapNullValue), false);
            //返回注册信息
            result.put("data", userPackage.get("id"));
        } catch (Exception e) {
            result.put("errorCode", "REGISTRATION_FAILED");
            result.put("errorMessage", ExceptionStackTrace.getExceptionInformation(e, ExceptionStackTrace.length(e) - 1));
        }
        //返回数据
        return result;
    }

    /**
     * 注销方法
     *
     * @return 注销结果
     */
    public JSONObject cancelAccount() {
        JSONObject result = new JSONObject();
        try {
            //判断是否登录
            if (user == null) {
                throw new RuntimeException("请先登录");
            }
            //删除注册信息
            Integer registrationInformationDeletionResult = database.dbDelete("users", user.getId());
            //删除用户配置文件
            boolean userProfileDeletionResult = new File(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + user.getId() + ".json").delete();
            //更新信息
            if (registrationInformationDeletionResult == 1 && userProfileDeletionResult) {
                this.user = null;
                this.userPackage = null;
            }
            //返回注销信息
            result.put("data", registrationInformationDeletionResult == 1 && userProfileDeletionResult);
        } catch (Exception e) {
            result.put("errorCode", "LOGOUT_FAILED");
            result.put("errorMessage", ExceptionStackTrace.getExceptionInformation(e, ExceptionStackTrace.length(e) - 1));
        }
        //返回数据
        return result;
    }

    /**
     * 更新注册信息方法
     *
     * @param record 记录
     * @return 更新结果
     */
    public JSONObject updateRegistrationInformation(JSONObject record) {
        JSONObject result = new JSONObject();
        try {
            //判断是否登录
            if (user == null) {
                throw new RuntimeException("请先登录");
            }
            List<String> fieldsName = new ArrayList<>();
            for (Field field : Users.class.getDeclaredFields()) {
                fieldsName.add(field.getName());
            }
            for (String key : record.keySet()) {
                if (!fieldsName.contains(key)) {
                    record.remove(key);
                }
            }
            record.toJavaObject(Users.class);
            //更新注册信息
            Integer updateRegistrationInformationResult = database.dbUpdate("users", user.getId(), record);
            //更新信息
            if (updateRegistrationInformationResult == 1) {
                this.user = getUserInfo("select * from users where id = '" + user.getId() + "';");
            }
            //返回更新结果
            result.put("data", updateRegistrationInformationResult == 1);
        } catch (Exception e) {
            result.put("errorCode", "LOGOUT_FAILED");
            result.put("errorMessage", ExceptionStackTrace.getExceptionInformation(e, ExceptionStackTrace.length(e) - 1));
        }
        //返回数据
        return result;
    }

    /**
     * 更新用户配置方法
     *
     * @param userPackage 用户配置
     * @return 更新结果
     */
    public JSONObject updateUserPackage(JSONObject userPackage) {
        JSONObject result = new JSONObject();
        try {
            //判断是否登录
            if (user == null) {
                throw new RuntimeException("请先登录");
            }
            //检查用户配置
            SchemaLoader.load(new org.json.JSONObject(JSONFileOperate.readJson(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + "usersPackage.json"))).validate(new org.json.JSONObject(JSONObject.toJSONString(userPackage, SerializerFeature.WriteMapNullValue)));
            //更新用户配置
            userPackage.put("id", user.getId());
            JSONFileOperate.writeJson(System.getProperty("java.class.path").substring(0, System.getProperty("java.class.path").indexOf("SPMSYS\\") + 7) + "usersPackage\\" + userPackage.get("id") + ".json", JSONObject.toJSONString(userPackage, SerializerFeature.WriteMapNullValue), false);
            this.userPackage = userPackage;
            //返回更新结果
            result.put("data", userPackage.get("id"));
        } catch (Exception e) {
            result.put("errorCode", "LOGOUT_FAILED");
            result.put("errorMessage", ExceptionStackTrace.getExceptionInformation(e, ExceptionStackTrace.length(e) - 1));
        }
        //返回数据
        return result;
    }

    /**
     * 数据库鉴权
     *
     * @param tableName  表名
     * @param methodName 方法名
     * @return 鉴权结果
     */
    public Boolean databaseAuth(String tableName, String methodName) {
        try {
            //判断是否登录
            if (user == null) {
                return false;
            }
            JSONObject database = userPackage.getJSONObject("auth").getJSONObject("database");
            Set<String> keys = database.keySet();
            //通配符获取数据库所有权限
            if (keys.contains("*")) {
                return true;
            }
            for (String table : database.keySet()) {
                JSONArray method = database.getJSONArray(table);
                int size = method.size();
                if (table.equals(tableName)) {
                    //通配符获取表所有权限
                    if (method.contains("*")) {
                        return true;
                    }
                    for (int i = 0; i < size; i++) {
                        //如果包含该方法则拥有权限
                        if (method.getString(i).equals(methodName)) {
                            return true;
                        }
                    }
                }
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }
}
